#include <config.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <asm/asm.h>

#include "ns16550.h"

#define msize		s2

/*
 * Register usage:
 *
 * s0 link versus load offset, used to relocate absolute adresses.
 * s1 free
 * s2 memory size.
 */

#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)
	.set   mips64
FEXPORT(lowlevel_init)
	move  fp, ra
	nop
	move  ra, fp
	jr fp
	nop
#else

	.set   mips64
FEXPORT(lowlevel_init)
	move  fp, ra

#if 1
	move   s0, zero

	mfc0   t0, CP0_STATUS
	li     t1, 0x64000000|ST0_KX|ST0_SX|ST0_UX|ST0_BEV      # {cu3,cu2,cu1,cu0}<={0110, status_fr<=1,0xe0 to enable 64bit space
	or     t0, t0, t1
	mtc0   t0, CP0_STATUS
	mtc0   zero, CP0_CAUSE
#endif

	//SPI init
	li   v0, 0xbfd00000
	li   v1, 0xff
	sb   v1, 0x5(v0) //set all spi cs to 1, default input
	li   v1, 0x47
	sb   v1, 0x4(v0) //spi speedup
	li   v1, 0x01
	sb   v1, 0x6(v0)

#if 0
	/*cfg pcie */
	li	t0, 0xbfe10540
	dli	t1, 0x14fff1002
	sd	t1, 0x0(t0)
	sd	t1, 0x20(t0)

	dli	t1, 0x14fff1102
	sd	t1, 0x0(t0)
	sd	t1, 0x20(t0)

	dli	t1, 0x14fff1202
	sd	t1, 0x0(t0)
	sd	t1, 0x20(t0)

	dli	t1, 0x14fff1302
	sd	t1, 0x0(t0)
	sd	t1, 0x20(t0)

	/*pcie 0 port 0*/
	dli	t0, 0x900000fe0800000c
	li	t1, 0xfff9ffff
	lw	t2, 0x0(t0)
	and	t1, t1, t2
	or	t1, 0x20000
	sw	t1, 0x0(t0)

	dli	t0, 0x900000fe0700001c
	lw	t2, 0x0(t0)
	li	t1, (0x1 << 26)
	or	t2, t1
	sw	t2, 0x0(t0)

	dli	t0, 0x9000000000000000
	li	t1, 0x16000000
	or	t0, t0, t1

	li	t1, (0x7<<18)|(0x7<<2)
	not	t1, t1
	lw	t2, 0x54(t0)
	and	t2, t2, t1
	sw	t2, 0x54(t0)

	lw	t2, 0x58(t0)
	and	t2, t2, t1
	sw	t2, 0x58(t0)

	dli	t1, 0xff204f
	sw	t1, 0x0(t0)

	/*pcie 0 port 1*/
	dli	t0, 0x900000fe0800080c
	li	t1, 0xfff9ffff
	lw	t2, 0x0(t0)
	and	t1, t1, t2
	or	t1, 0x20000
	sw	t1, 0x0(t0)

	dli	t0, 0x900000fe0700081c
	lw	t2, 0x0(t0)
	li	t1, (0x1 << 26)
	or	t2, t1
	sw	t2, 0x0(t0)

	dli	t0, 0x9000000000000000
	li	t1, 0x16001000
	or	t0, t0, t1

	li	t1, (0x7<<18)|(0x7<<2)
	not	t1, t1
	lw	t2, 0x54(t0)
	and	t2, t2, t1
	sw	t2, 0x54(t0)

	lw	t2, 0x58(t0)
	and	t2, t2, t1
	sw	t2, 0x58(t0)

	dli	t1, 0xff204f
	sw	t1, 0x0(t0)

#if 0
//force link 1.0 prot0
//#if defined(FORCE_PCIE_GEN1) && FORCE_PCIE_GEN1 & 1
	dli	t1, 0x900000fe00000000
	lw	t2, 0xa0(t1)
	or	t2, 3
	xor	t2, 2
	sw	t2, 0xa0(t1)
//#endif

//force link 1.0 prot1
//#if defined(FORCE_PCIE_GEN1) && FORCE_PCIE_GEN1 & 2
	dli	t1, 0x900000fe00000800
	lw	t2, 0xa0(t1)
	or	t2, 3
	xor	t2, 2
	sw	t2, 0xa0(t1)
//#endif
#endif

#if 0
	li	t1, 0xbfe10554
	lw	t2, 0x0(t1)
	or	t2, (0x7f<<13)|(0x6f<<7)
	sw	t2, 0x0(t1)
#endif

//set pcie bar0 of ep
	li	t1, 0xbfe10120
	lw	t2, 0(t1)
	li 	t1, (1<<6)|(1<<11)
	and 	t2, t1
	beqz	t2,2f
	nop
//set base
	li	t1, 0xb6000000
	/*set ep device 0,1 class type to 0x0600*/
	li	t2, 0x21ff204f
	sw	t2, 0(t1)
	li	v0, 0xb6800000
	li	v1, 0x06000001
	sw	v1, 8(v0)
	sw	v1, 0x1008(v0)
	li	t2, 0x01ff204f
	sw	t2, 0(t1)
	/*set ep device 0,1 bar size to 256M*/
	li	v1, 0x0fffffff
	sw	v1, 0x110(v0)
	sw	v1, 0x1110(v0)
	li	t2, 0xf0000
	sw	t2, 0x68(t1)
	li	t2, 0x00000
	sw	t2, 0x70(t1)
2:
	/*set pcie controler device 0 bar size to 4K*/
	dli	t0, 0x900000fe00000000
	li  t1, 0x00000fff
	sw  t1, 0x110(t0)
	dli	t0, 0x900000fe00000800
	sw  t1, 0x110(t0)
#endif

	/*enable dma/pcie/dc/gmac/sata/usb/hda device cache*/
	li	t0, 0xbfe10110
	lw	t1, 0x0(t0)
	li	t2, 0x12fe8
	or	t1, t2
	sw	t1, 0x0(t0)

#if 1
	/*cfg pins to main, uart2 use GPIO 60/61*/
	li	t0, 0xbfe104ac
	li	t1, 0
	li	t2, 0x770000
	sw	t2, 0x0(t0)
#endif

	//初始化调试串口，2k没配置前的启动频率默认100MHz
	li  a0, (OSC_CLK / 16) / CONFIG_BAUDRATE
	bal initserial
	nop
	PRINTSTR("\r\nserial init ok\r\n")
	nop

##define VOLTIGE_CTRL
#ifdef VOLTIGE_CTRL
	move	s1, zero
	TTYDBG("\r\nnode 0 N Voltage  write :\r\n")
	bal	v_n_ctrl
	nop
	TTYDBG("\r\nnode 0 N Voltage  read :\r\n")
	bal	v_n_ctrl_read
	nop
#endif

#include "ls2k500_clk_config.S"

	/* enable kseg0 cachability */
	mfc0 a0, CP0_CONFIG
	ori  a0, a0, 0x3      //cached
	mtc0 a0, CP0_CONFIG

#ifdef USEPCI
	li	t0, 0xbfe10430
	lw	a2, 0x0(t0)
	// pcie0 and pcie1
	lui	t1, 0x3
#endif

#ifndef CONFIG_LOONGSON_DDR
//##########################################
//DDR config start
//cxk
#include "mm/lsmc_ddr_param_define.h"
#include "mm/ddr_config_define.h"
//#define DDR_DLL_BYPASS
#define DISABLE_DIMM_ECC
#ifdef  ARB_LEVEL
#define AUTO_ARB_LEVEL
#endif
#ifdef  AUTO_ARB_LEVEL
//#define CHECK_ARB_LEVEL_FREQ
#ifdef  AUTO_DDR_CONFIG
#define CHECK_ARB_LEVEL_DIMM
#endif
//#define DEBUG_AUTO_ARB_LEVEL
#endif
//#define DEBUG_DDR
//#define DEBUG_DDR_PARAM
//#define PRINT_DDR_LEVELING

#ifdef  AUTO_DDR_CONFIG
#include "mm/i2c.h"
#endif

	TTYDBG("\r\nInit Memory begin, wait a while......\r\n")
####################################
	move   msize, $0
	move   s3, $0
//!!!!important--s1 must be correctly set
#ifdef  AUTO_DDR_CONFIG
	dli   s1, 0xff100004  //set use MC1 or MC0 or MC1/0 and give All device id
#elif defined(DDR_S1)
	dli   s1, DDR_S1
#else
//	dli   s1, 0xc2e30400c2e30404
//	dli   s1, 0xc1a10404
//	dli   s1, 0xc0a10400c0a10400
//	dli   s1, 0xc0a18404
//	dli   s1, 0xf0a31004
//	dli   s1, 0xc1a10404
	dli   s1, (MC_SDRAM_TYPE_DDR3    /* sdram type: DDR3/DDR2 */ \
		| MC_DIMM_ECC_NO        /* dimm ECC: YES/NO */ \
		| MC_DIMM_BUF_REG_NO    /* dimm buffer register: YES/NO, for RDIMM use YES, all else use NO*/ \
		| MC_DIMM_WIDTH_64      /* memory data width: 64/32 */ \
		| MC_SDRAM_ROW_16       /* sdram row address number: 15~11 */ \
		| MC_SDRAM_COL_10       /* sdram column address number: 12~9 */ \
		| MC_SDRAM_BANK_8       /* sdram bank number: 8/4 */ \
		| MC_ADDR_MIRROR_NO    /* for standard DDR3 UDIMM, use YES, else use NO */ \
		| MC_SDRAM_WIDTH_X16     /* SDRAM device data width: 8/16 */ \
		| MC_USE_CS_0         /* the CS pins the sdram connected on(split by '_', from small to big) */ \
		| MC_MEMSIZE_(8)        /* MC memory size, unit: 512MB */ \
		| USE_MC_0)
#endif

#include "mm/loongson3_ddr2_config.S"

	/*judge the node0 whether have memory*/
	and     a0, msize, 0xff

	//close default internal mapping in ddr controller
	li      t0, 0xbfe10100
	lw      a0, 0x0(t0)
	and     a0, a0, 0xffffe7ff
	ori     a0, a0, 0x00000800
	sw      a0, 0x0(t0)
	sync

	TTYDBG("\r\nInit Memory success\r\n")
#endif //CONFIG_LOONGSON_DDR

bootnow:
	move  k1, msize
	move  ra, fp
	jr    ra
	nop

LEAF(initserial)
.set noat
	move AT, ra

	li   v0, UART_BASE_ADDR
	li   v1, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4
	sb   v1, NSREG(NS16550_FIFO)(v0)
	li   v1, CFCR_DLAB                  #DLAB
	sb   v1, NSREG(NS16550_CFCR)(v0)
	sb   a0, NSREG(NS16550_DATA)(v0)
	srl  a0, 8
	sb   a0, NSREG(NS16550_IER)(v0)     #set BRDH
	li   v1, CFCR_8BITS                 #8bit
	sb   v1, NSREG(NS16550_CFCR)(v0)
	li   v1, MCR_DTR|MCR_RTS
	sb   v1, NSREG(NS16550_MCR)(v0)
	li   v1, 0x0
	sb   v1, NSREG(NS16550_IER)(v0)

	jr   ra
	nop
.set at
END(initserial)

LEAF(printk)
	.set noreorder
	move  a2, ra
	move  a1, a0
	lbu   a0, 0(a1)
1:
	beqz  a0, 2f
	nop
	bal   tgt_putchar
	nop
	addiu a1, 1
	lbu   a0, 0(a1)
	b     1b
	nop
2:
	jr    a2
	nop
	.set reorder
END(printk)

tgt_putchar:
	la   v0, UART_BASE_ADDR
1:
	lbu  v1, NSREG(NS16550_LSR)(v0)
	and  v1, LSR_TXRDY
	beqz v1, 1b
	nop

	sb   a0, NSREG(NS16550_DATA)(v0)
	jr   ra
	nop

	.rdata
hexchar:
	.ascii	"0123456789abcdef"
	.text

#ifdef PRINT_MSG
LEAF(hexserial)
	move  a2, ra
	move  a1, a0
	li    a3, 8
1:
	rol   a0, a1, 4
	move  a1, a0
	and   a0, 0xf
	la    t0, hexchar
	addu  t0, s0
	addu  t0, a0
	lbu   a0, 0(t0)
	bal   tgt_putchar

	addu  a3, -1
	bnez  a3, 1b
	nop

	jr    a2
	nop
END(hexserial)
#else
LEAF(hexserial)
	move  a2, ra
	jr    a2
	nop
END(hexserial)
#endif

LEAF(hexserial64)
	move	ta3, ra
	jr	ta3
   nop
END(hexserial64)

#ifndef CONFIG_LOONGSON_DDR
#ifdef AUTO_DDR_CONFIG
#include "mm/i2c.S"
#include "mm/detect_node_dimm_all.S"
#endif

#include "mm/ls3A8_ddr_config.S"
#ifdef DDR3_DIMM
#include "mm/loongson3C_ddr3_leveling.S"
#endif

	.align 5
#include "mm/loongson_mc2_param.S"
#endif //CONFIG_LOONGSON_DDR

#endif //#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)

#ifdef CONFIG_32BIT
	/* u64  __raw__readq(u64 addr)
	 * a0, a1 hold low 32 and high 32
	 * v0, v1 hold low 32 and high 32 of ret
	 */
	.text
	.global __raw__readq
	.ent    __raw__readq
__raw__readq:
	dsll32  a1, a1, 0
	dsll32  a0, a0, 0
	dsrl32  a0, a0, 0
	or      a0, a1, a0

	ld      v0, 0(a0)
	dsra32  v1, v0, 0
	jr      ra
	sll     v0, v0, 0
.end    __raw__readq

	/* u64 __raw__writeq(u64 addr, u64 val)
	 * a0, a1 hold low 32 and high 32 of addr,
	 * a2, a2 hold low 32 and high 32 of val,
	 * v0, v1 hold low 32 and high 32 of ret
	 */
	.global __raw__writeq
	.ent    __raw__writeq
__raw__writeq:
	dsll32  a1, a1, 0
	dsll32  a0, a0, 0
	dsrl32  a0, a0, 0
	or      a0, a1, a0

	dsll32  a3, a3, 0
	dsll32  a2, a2, 0
	dsrl32  a2, a2, 0
	or      a2, a2, a3

	sd      a2, 0(a0)
//	ld      v0, 0(a0)
//	dsra32  v1, v0, 0
	jr      ra
//	sll     v0, v0, 0
	nop
.end    __raw__writeq

	/* u64  __raw__readw(u64 addr)
	 * a0, a1 hold low 32 and high 32
	 * v0     hold     32 of ret
	 */
	.global __raw__readw
	.ent	__raw__readw
__raw__readw:
	dsll32	a1, a1, 0
	dsll32	a0, a0, 0
	dsrl32	a0, a0, 0
	or	a0, a1, a0

	lw	v0, 0(a0)
	jr	ra
	sll	v0, v0, 0
.end	__raw__readw


	/* u64 __raw__writew(u64 addr, u32 val)
	 * a0, a1 hold low 32 and high 32 of addr,
	 * a2     hold 32 of val,
	 * v0     hold 32 of ret
	 */
	.global __raw__writew
	.ent	__raw__writew
__raw__writew:
	dsll32	a1, a1, 0
	dsll32	a0, a0, 0
	dsrl32	a0, a0, 0
	or	a0, a1, a0

	dsll32	a2, a2, 0
	dsrl32	a2, a2, 0

	sw	a2, 0(a0)
//	lw	v0, 0(a0)
	jr	ra
//	sll	v0, v0, 0
	nop
.end	__raw__writew

	/* u64  __raw__readh(u64 addr)
	 * a0, a1 hold low 32 and high 32
	 * v0     hold     16 of ret
	 */
	.global __raw__readh
	.ent	__raw__readh
__raw__readh:
	dsll32	a1, a1, 0
	dsll32	a0, a0, 0
	dsrl32	a0, a0, 0
	or	a0, a1, a0

	lh	v0, 0(a0)
	jr	ra
	sll	v0, v0, 0
.end	__raw__readh


	/* u64 __raw__writeh(u64 addr, u16 val)
	 * a0, a1 hold low 32 and high 32 of addr,
	 * a2     hold 16 of val,
	 * v0     hold 16 of ret
	 */
	.global __raw__writeh
	.ent	__raw__writeh
__raw__writeh:
	dsll32	a1, a1, 0
	dsll32	a0, a0, 0
	dsrl32	a0, a0, 0
	or	a0, a1, a0

	dsll32	a2, a2, 0
	dsrl32	a2, a2, 0

	sh	a2, 0(a0)
//	lh	v0, 0(a0)
	jr	ra
//	sll	v0, v0, 0
	nop
.end	__raw__writeh

	/* u64  __raw__readb(u64 addr)
	 * a0, a1 hold low 32 and high 32
	 * v0     hold     8 of ret
	 */
	.global __raw__readb
	.ent	__raw__readb
__raw__readb:
	dsll32	a1, a1, 0
	dsll32	a0, a0, 0
	dsrl32	a0, a0, 0
	or	a0, a1, a0

	lb	v0, 0(a0)
	jr	ra
	sll	v0, v0, 0
.end	__raw__readb

	/* u64 __raw__writeb(u64 addr, u8 val)
	 * a0, a1 hold low 32 and high 32 of addr,
	 * a2     hold 8 of val,
	 * v0     hold 8 of ret
	 */
	.global __raw__writeb
	.ent	__raw__writeb
__raw__writeb:
	dsll32	a1, a1, 0
	dsll32	a0, a0, 0
	dsrl32	a0, a0, 0
	or	a0, a1, a0

	dsll32	a2, a2, 0
	dsrl32	a2, a2, 0

	sb	a2, 0(a0)
//	lb	v0, 0(a0)
	jr	ra
//	sll	v0, v0, 0
	nop
.end	__raw__writeb
#endif //CONFIG_32BIT